<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 写模板  -->
  <div id="root">
    <p>name: {{name.firstName}}=== {{name.lastName}}</p>
  </div>

  <script>
    /*
    1.getValueByPath解决了obj.name.firstName的取值问题
    2.虚拟dom的问题，真实dom转为虚拟dom，虚拟dom转为真实dom（深浅拷贝）
    */ 
    function getValueByPath (obj, path) {
      let paths = path.split('.');
      let res = obj;
      let prop;
      while(prop = paths.shift()) {
        res = res[prop]
      }
      return res;
    }
  
    let rkuohao = /\{\{(.+?)\}\}/g; // {{}} 的正则表达式

    // {{}} 插值表达式替换为真实data中的数据
    function compiler(template, data) {
      let childNodes = template.childNodes; // 获取子元素
      for(let i =0; i<childNodes.length; i++) {
        let type = childNodes[i].nodeType; //获取元素属性 1 元素，3 文本节点
        if(type === 3) {
          // 文本节点，可以判断里边是否有{{}} 插值
          let txt = childNodes[i].nodeValue; // 该属性只有文本节点才有意义
          // 判断有没有花括号
          txt = txt.replace(rkuohao, function(_, g) {
            /*
            {{}}中是单层数据，没有多层嵌套的 .
            let key = g.trim(); // 写在花括号里边的东西
            let value = data[key];
            下边是{{xxx.yyy.zzz}}多层嵌套的封装,获取data中对应的值
            */
            let path = g.trim();
            let value = getValueByPath(data, path)
            return value;
          })

          // 注意： txt现在和dom元素是没有关系的
          childNodes[i].nodeValue = txt; // 将dom元素的{{}}替换成data中的数据
        } else if(type === 1) {
          // 子元素是元素，考虑它有没有子元素，是否需要将其子元素进行 插值替换
          compiler(childNodes[i], data)
        }
      }
    }

    function JGVue(options) {
      // 习惯（vue内部规范）内部使用的数据，私有的使用_开头，可读可写，只读数据$开头
      this._data = options.data;
      this._el = options.el;

      // 准备工作（准备模板)
      this._templateDom = document.querySelector(this._el)
       // el 的父元素，用于将真实dom替换
      this._parent = this._templateDom.parentNode;
      // 渲染工作
      this.render()
    }
    
    /* 将模板 结合 数据，得到真实dom添加到页面中 */ 
    JGVue.prototype.render = function() {
      this.compiler()
    }
    
    // 渲染模板,编译，将模板与数据结合得到真正的dom
    JGVue.prototype.compiler = function () {
      let realHTMLDom = this._templateDom.cloneNode(true)
      compiler(realHTMLDom, this._data)
      this.update(realHTMLDom)
    }

    // 将dom元素更新到页面中
    JGVue.prototype.update = function (real) {
      this._parent.replaceChild(real, document.querySelector('#root'))
    }

    // 参考vue的使用
    let app = new JGVue({
      el: '#root',
      data:{
        name: {
          firstName: '张',
          lastName: '三'
        }
      }
    })



  </script>
  <script>
    // 通过xxx.yyy.zzz的方式获取data中的数据，原始1
     /*
    使用'xxx.yyy.zzz'可以访问到对象的成员
    需要用字符串路径访问到对象的成员
    */
    function getValueByPath (obj, path) {
      let paths = path.split('.'); // [xxx, yyy, zzz]
      /*
      先取得obj.xxx，在取得结果中的yyy，再取得结果中的zzz
      let res = null;
      res = obj[paths[0]]; ====== res = obj[xxx]
      res = res[paths[1]]; ====== res = obj[yyy]
      res = res[paths[2]]; ====== res = obj[zzz]
      */
      let res = obj;
      let prop;
      /*
      let arr = [1,2,3,4]
      let prop = arr.shift();
      console.log(prop);------1
      console.log(arr);------[2, 3, 4]
      */

      while(prop = paths.shift()) {
        res = res[prop]
      }
      return res;
    }
    
    let obj1 = {
      xxx: {
        yyy: {
          zzz: 'zzz'
        }
      }
    }
    let objVal = getValueByPath(obj1, 'xxx.yyy.zzz');
    console.log('objVal===='+ objVal)
    
  </script>
  <script>
     // 通过xxx.yyy.zzz的方式获取data中的数据，函数颗粒化实现方式，vue源码做了优化，是这种方式
      /*
    vue中的模板是不会改变的，但是data中的数据是经常变化的，path一旦写到代码中，就基本不变了，
    但是数据obj会经常改变，
    数据颗粒化  createGetValueByPath
    函数是在vue编译模板的时候就生成了
    */

    function createGetValueByPath(path) {
      let paths = path.split('.'); // [xxx,yyy,zzz]
      return function getValueByPath(obj) {
        let res = obj;
        let prop;
        while(prop = paths.shift()) {
          res = res[prop]
        }
        return res;
      }
    }

    let o = {
      a: {
        b: {
          c: {
            d: {
              e:'eeeee'
            }
          }
        }
      }
    }
    let getValueByPath = createGetValueByPath('a.b.c')

    let res = getValueByPath(o)
    console.log(res)
  </script>
</body>
</html>